// (C) 2021 Intel Corporation. All rights reserved.
// Your use of Intel Corporation's design tools, logic functions and other 
// software and tools, and its AMPP partner logic functions, and any output 
// files from any of the foregoing (including device programming or simulation 
// files), and any associated documentation or information are expressly subject 
// to the terms and conditions of the Intel Program License Subscription 
// Agreement, Intel FPGA IP License Agreement, or other applicable 
// license agreement, including, without limitation, that your use is for the 
// sole purpose of programming logic devices manufactured by Intel and sold by 
// Intel or its authorized distributors.  Please refer to the applicable 
// agreement for further details.

`timescale 1 ns / 1 ps

module espi_vw_gpi_update
  #(
    parameter [7:0] BASE = 8'h00
    )
    (
     output logic        no_gpi_changes,
     input logic [127:0] gpi,
     output logic        request,
     input logic         grant,
     input logic         full,
     output logic        put,
     output logic [15:0] wdata,
     input logic         clk,
     input logic         reset_n
     );

    logic [127:0]        vw_gpi;
    logic [31:0]         gpi_changed;
    logic [7:0]          i_gpi;
    logic [2:0]          gpi_search_state;
    logic [7:0]          index;
    logic [3:0]          valid;
    logic [3:0]          value;

    
    always @(posedge clk, negedge reset_n) begin
        if (!reset_n) begin
            no_gpi_changes   <= 1'b1;
            put              <= 0;
            wdata            <= 0;
            vw_gpi           <= 0;
            i_gpi            <= 0;
            gpi_changed      <= 0;
            request          <= 0;
            gpi_search_state <= 0;
        end
        else begin
            case (gpi_search_state)
              0: begin
                  i_gpi   <= 0;
                  request <= 0;
                  put     <= 0;
                  for (int i=0; i < 32; i++) begin
                      gpi_changed[i] <= |(vw_gpi[i*4+:4] ^ gpi[i*4+:4]);
                  end

                  if (!full) begin
                      gpi_search_state <= 1;
                  end
              end // case: 0

              1: begin
                  if (gpi_changed) begin
                      no_gpi_changes   <= 0;
                      gpi_search_state <= 2;
                      if (gpi_changed[15:0] == 0) begin
                          i_gpi <= 8'd16;
                      end
                  end
                  else begin
                      no_gpi_changes   <= 1'b1;
                      gpi_search_state <= 0;
                  end // else: !if(gpi_changed)
              end // case: 1

              2: begin
                  gpi_search_state <= 3;
                  if (gpi_changed[i_gpi+:8] == 0) begin
                      i_gpi <= i_gpi+8'd8;
                  end
              end

              3: begin
                  gpi_search_state <= 4;
                  if (gpi_changed[i_gpi+:4] == 0) begin
                      i_gpi <= i_gpi+8'd4;
                  end
              end

              4: begin
                  gpi_search_state <= 5;
                  if (gpi_changed[i_gpi+:2] == 0) begin
                      i_gpi <= i_gpi+8'd2;
                  end
              end

              5: begin
                  gpi_search_state <= 6;
                  if (gpi_changed[i_gpi+:1] == 0) begin
                      i_gpi <= i_gpi+8'd1;
                  end
              end

              6: begin
                  request <= 1;
                  if (grant) begin
                      wdata              <= {valid, value, index};
                      put                <= 1;
                      vw_gpi[i_gpi*4+:4] <= gpi[i_gpi*4+:4];
                      gpi_search_state   <= 0;
                  end
              end

              default: begin
                  gpi_search_state <= 0;
              end

            endcase // case (gpi_search_state)

            if (full) begin
                // Force a return to the initial state until the FIFO is available.
                gpi_search_state <= 0;
            end
        end // else: !if(!reset_n)
    end // always @ (posedge clk, negedge reset_n)

    assign index = i_gpi+8'h80+BASE[7:0];
    assign valid = vw_gpi[i_gpi*4+:4] ^ gpi[i_gpi*4+:4];
    assign value = gpi[i_gpi*4+:4];

endmodule // espi_vw_gpi_update
